package com.yunzhou.controller.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.yunzhou.constants.Constants;
import com.yunzhou.constants.RedisConstants;
import com.yunzhou.dto.*;
import com.yunzhou.entity.ForumArticle;
import com.yunzhou.entity.ForumArticleAttachment;
import com.yunzhou.entity.ForumArticleSetting;
import com.yunzhou.entity.UserInfo;
import com.yunzhou.enums.ForumArticleAttachmentStatusEnum;
import com.yunzhou.enums.ForumArticleStatusEnum;
import com.yunzhou.enums.ForumArticleTopTypeEnum;
import com.yunzhou.enums.LikeRecordOpTypeEnum;
import com.yunzhou.mapper.LikeRecordMapper;
import com.yunzhou.properties.ElasticSearchProperties;
import com.yunzhou.properties.JwtProperties;
import com.yunzhou.result.Result;
import com.yunzhou.service.*;
import com.yunzhou.utils.JwtUtil;
import com.yunzhou.utils.MyStringUtil;
import com.yunzhou.utils.ThreadLocalUtil;
import com.yunzhou.vo.ForumArticleDetailVO;
import com.yunzhou.vo.ForumArticleVO;
import com.yunzhou.vo.HotForumArticleVO;
import io.minio.GetObjectResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/user/forumArticle")
public class ForumArticleController {

    @Autowired
    private ForumArticleService forumArticleService;
    @Autowired
    private ForumArticleAttachmentService forumArticleAttachmentService;
    @Resource
    private ForumArticleSettingService forumArticleSettingService;
    @Autowired
    private UserService userService;
    @Autowired
    private LikeRecordMapper likeRecordMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private ElasticSearchService elasticSearchService;
    @Resource
    private MinioService minioService;

    // 获取文章列表 0:推荐 1:最新发布
    @GetMapping("/getForumArticleList")
    public Result getForumArticleList(ForumArticleDTO forumArticleDTO){
        System.out.println("接收到参数："+forumArticleDTO);

        // 获取文章列表
        List<ForumArticle> articleList = forumArticleService.getForumArticleList(
                forumArticleDTO.getBoardId(), forumArticleDTO.getParentBoardId(),
                forumArticleDTO.getBoardName(), forumArticleDTO.getParentBoardName(),
                forumArticleDTO.getOrderType(), forumArticleDTO.getPageNum(), forumArticleDTO.getPageSize());

        // 获取文章总数
        Integer articleArticleListTotal = forumArticleService.getForumArticleListTotal(
                forumArticleDTO.getBoardId(), forumArticleDTO.getParentBoardId(),
                forumArticleDTO.getBoardName(), forumArticleDTO.getParentBoardName());

        ForumArticleVO forumArticleVO = new ForumArticleVO();
        forumArticleVO.setForumArticleList(articleList);
        forumArticleVO.setTotal(articleArticleListTotal);
        return Result.success(forumArticleVO);
    }

    // 获取文章详情
    @GetMapping("/getForumArticleDetail")
    @Transactional
    public Result getArticleDetail(HttpServletRequest request, Long articleId){
        System.out.println("文章" + articleId);

        // 准备返回值
        ForumArticleDetailVO forumArticleDetailVO = new ForumArticleDetailVO();

        // 1、文章阅读数+1
        forumArticleService.updateForumArticleReadCountByArticleId(articleId, 1);

        // 2、获取文章内容
        ForumArticle forumArticle = forumArticleService.getForumArticleByArticleId(articleId);
        forumArticleDetailVO.setForumArticle(forumArticle);

        // 3、再获取文章附件（首先文章得有附件再去查）
        if(forumArticle.getAttachmentStatus().equals(ForumArticleAttachmentStatusEnum.HAVEATTACHMENT.getStatus())){
            forumArticleDetailVO.setForumArticleAttachmentList(
                                    forumArticleAttachmentService.getForumArticleAttachmentListByArticleId(articleId));
        }

        // 4、获取文章相关设置
        ForumArticleSetting fas = forumArticleSettingService.selectByArticleId(articleId);
        forumArticleDetailVO.setPreviewTheme(fas.getPreviewTheme());
        forumArticleDetailVO.setCodeTheme(fas.getCodeTheme());

        // 5、查看当前登录用户是否点赞过该文章
        String token = request.getHeader(jwtProperties.getTokenName());
        System.out.println("从请求头中获取的token为："+token);
        if (token == null) {
            // 当前用户未登录
            forumArticleDetailVO.setHaveLike(false);
            return Result.success(forumArticleDetailVO);
        }

        // 如果用户Token过期，则也返回点赞结果为false
        String tokenKey = RedisConstants.LOGIN_TOKEN_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);
        if (userMap.isEmpty()) {
            // 没有通过token找到Redis中数据，说明Token已过期
            forumArticleDetailVO.setHaveLike(false);
            return Result.success(forumArticleDetailVO);
        }

        // 从redis中获取相同的token
        String redisToken = stringRedisTemplate.opsForHash().get(tokenKey, "token").toString();
        if(redisToken == null) {
            // 当前用户未登录
            forumArticleDetailVO.setHaveLike(false);
            return Result.success(forumArticleDetailVO);
        }

        // 获取Token中存储的用户信息
        // Map<String, Object> claims = JwtUtil.parseToken(jwtProperties.getSecretKey(),token);
        // Long userId = Long.valueOf(claims.get("userId").toString());
        Long userId = Long.parseLong(stringRedisTemplate.opsForHash().get(tokenKey, "userId").toString());
        if (userService.selectByUserId(userId) == null) {
            // 如果当前用户不存在
            forumArticleDetailVO.setHaveLike(false);
            return Result.success(forumArticleDetailVO);
        }

        System.out.println("查询文章详情的当前登录的用户ID为：" + userId);
        if (likeRecordMapper.getLikeRecordByObjectIdAndUserIdAndOpType(articleId, userId, LikeRecordOpTypeEnum.ARTICLE_LIKE.getOpType()) == null) {
            // 当前用户未点赞
            forumArticleDetailVO.setHaveLike(false);
        } else {
            forumArticleDetailVO.setHaveLike(true);
        }

        return Result.success(forumArticleDetailVO);
    }

    // 根据文章id获取文章以及文章附件（修改文章用，回显）
    @GetMapping("/getForumArticleDetailForUpdate")
    public Result getForumArticleDetailForUpdate(HttpServletRequest request, Long articleId){
        System.out.println("文章" + articleId);

        // 准备返回值
        ForumArticleDetailVO forumArticleDetailVO = new ForumArticleDetailVO();

        // 1、获取文章内容
        ForumArticle forumArticle = forumArticleService.getForumArticleByArticleId(articleId);
        forumArticleDetailVO.setForumArticle(forumArticle);

        // 2、获取文章附件（如果有）
        if(forumArticle.getAttachmentStatus().equals(ForumArticleAttachmentStatusEnum.HAVEATTACHMENT.getStatus())){
            forumArticleDetailVO.setForumArticleAttachmentList(forumArticleAttachmentService.getForumArticleAttachmentListByArticleId(articleId));
        }

        // 3、获取文章设置相关内容
        ForumArticleSetting forumArticleSetting = forumArticleSettingService.selectByArticleId(articleId);
        if (forumArticleSetting != null){
            forumArticleDetailVO.setPreviewTheme(forumArticleDetailVO.getPreviewTheme());
            forumArticleDetailVO.setCodeTheme(forumArticleDetailVO.getCodeTheme());
        }

        return Result.success(forumArticleDetailVO);
    }

    // 文章点赞/取消点赞
    @PostMapping("/doArticleLike")
    public Result doArticleLike(@RequestBody DoArticleLikeDTO doArticleLikeDTO){
        System.out.println("即将点赞的文章ID为："+doArticleLikeDTO.getArticleId());
        forumArticleService.doArticleLike(doArticleLikeDTO.getArticleId(), ThreadLocalUtil.getCurrentUserId());
        return Result.success();
    }

    // 文章内容上传图片
    @PostMapping("/uploadArticleContentImg")
    public Result uploadArticleContentImg(@RequestBody MultipartFile file){
//        // 文件名
//        String extendFileName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")); //.png
//        String imgName = "tmp/" + MyStringUtil.getUUID() + extendFileName;
//        System.out.println("文件名为：" + imgName);
//
//        // 返回图片存储路径
//        String s = qiniuyunService.uploadFile(file, imgName);
//        System.out.println("文件存储路径：" + s);

        // 这里打算用UUID生成文件名
        String uuid = MyStringUtil.getUUID();
        // 文件名为UUID+文件名后缀
        String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")); //.png
        String imgName = uuid + suffix;
        System.out.println("文件名为：" + imgName);

        String imagePath = null;
        try {
            imagePath = minioService.uploadFile(file , Constants.MINIO_ARTICLE_IMAGE_BUCKET_NAME, imgName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return Result.success(imagePath);
    }

    // 发表文章
    @PostMapping("/postArticle")
    @Transactional
    public Result postArticle(PostArticleDTO postArticleDTO) throws IOException {
        System.out.println("前端传递过来的文章参数为："+postArticleDTO);

        // 先将文章存储到数据库中
        UserInfo userInfo = userService.selectByUserId(ThreadLocalUtil.getCurrentUserId());

        ForumArticle forumArticle = new ForumArticle();
        forumArticle.setBoardId(postArticleDTO.getBoardId());
        forumArticle.setBoardName(postArticleDTO.getBoardName());
        forumArticle.setParentBoardId(postArticleDTO.getParentBoardId());
        forumArticle.setParentBoardName(postArticleDTO.getParentBoardName());
        forumArticle.setUserId(userInfo.getUserId());
        forumArticle.setNickname(userInfo.getNickname());
        forumArticle.setUserIpAddress(null);
        forumArticle.setTitle(postArticleDTO.getTitle());
        forumArticle.setCover(null); // 先置为null，一会赋值
        forumArticle.setContent(postArticleDTO.getContent());
        forumArticle.setMarkdownContent(postArticleDTO.getMarkdownContent());
        forumArticle.setEditorType(postArticleDTO.getEditorType());
        forumArticle.setSummary(postArticleDTO.getSummary());
        forumArticle.setPostTime(LocalDateTime.now());
        forumArticle.setLastUpdateTime(LocalDateTime.now());
        forumArticle.setReadCount(0);
        forumArticle.setLikeCount(0);
        forumArticle.setCommentCount(0);
        forumArticle.setTopType(ForumArticleTopTypeEnum.NOTOP.getTopType());
        forumArticle.setAttachmentStatus(postArticleDTO.getAttachmentStatus());
        forumArticle.setStatus(ForumArticleStatusEnum.AUDIT.getStatus()); // -1:已删除 0:待审核  1:已审核 (后期改)

        // 新增文章
        forumArticleService.insert(forumArticle);

        // 新增文章后，增加文章设置
        ForumArticleSetting fas = new ForumArticleSetting();
        fas.setArticleId(forumArticle.getArticleId());
        fas.setPreviewTheme(postArticleDTO.getPreviewTheme());
        fas.setCodeTheme(postArticleDTO.getCodeTheme());
        forumArticleSettingService.insert(fas);

        // 将文章上传到es中
        elasticSearchService.addForumArticleDocument(forumArticle);

        // 将文章封面上传到minio
        if(postArticleDTO.getCoverImg() != null){

            // 这里打算用UUID生成文件名
            String uuid = MyStringUtil.getUUID();
            // 文件名为UUID+文件名后缀
            String suffix = postArticleDTO.getCoverImg().getOriginalFilename()
                    .substring(postArticleDTO.getCoverImg().getOriginalFilename().lastIndexOf(".")); //.png
            String imgName = uuid + suffix;
            System.out.println("文件名为：" + imgName);

            String imagePath = null;
            try {
                imagePath = minioService.uploadFile(postArticleDTO.getCoverImg(), Constants.MINIO_ARTICLE_COVER_IMAGE_BUCKET_NAME, imgName);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            // 更新文章的封面
            forumArticle.setCover(imagePath);
            forumArticleService.update(forumArticle);
        }

        // 再将文章的附件上传到minio
        if(postArticleDTO.getAttachmentFile() != null){

            // 这里打算用UUID生成文件名
            String uuid = MyStringUtil.getUUID();
            // 文件名为UUID+文件名后缀
            String suffix = postArticleDTO.getAttachmentFile().getOriginalFilename()
                    .substring(postArticleDTO.getAttachmentFile().getOriginalFilename().lastIndexOf(".")); //.png
            String imgName = uuid + suffix;
            System.out.println("文件名为：" + imgName);

            String attachmentPath = null;
            try {
                attachmentPath = minioService.uploadFile(
                        postArticleDTO.getAttachmentFile(), Constants.MINIO_ARTICLE_ATTACHMENT_BUCKET_NAME, imgName);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            // 更新文章附件记录
            ForumArticleAttachment forumArticleAttachment = new ForumArticleAttachment();
            forumArticleAttachment.setArticleId(forumArticle.getArticleId());
            forumArticleAttachment.setUserId(userInfo.getUserId());
            forumArticleAttachment.setFileSize(postArticleDTO.getAttachmentFile().getSize());
            forumArticleAttachment.setFileName(postArticleDTO.getAttachmentFile().getOriginalFilename());
            forumArticleAttachment.setBucketName(Constants.MINIO_ARTICLE_ATTACHMENT_BUCKET_NAME);
            forumArticleAttachment.setObjectName(imgName);
            forumArticleAttachment.setDownloadCount(0);
            forumArticleAttachment.setFilePath(attachmentPath);
            forumArticleAttachment.setFileType(0);
            forumArticleAttachment.setIntegral(postArticleDTO.getIntegral());
            forumArticleAttachmentService.insert(forumArticleAttachment);
        }

        return Result.success(forumArticle);
    }

    // 更新文章
    @PostMapping("/updateForumArticle")
    public Result updateForumArticle(UpdateForumArticleDTO updateForumArticleDTO) throws Exception {
        System.out.println("即将更新的文章信息为：" + updateForumArticleDTO);
        ForumArticle forumArticle = BeanUtil.copyProperties(updateForumArticleDTO, ForumArticle.class);

        // 如果之前该文章没有封面，这次添加了封面，则需要上传封面到Minio
        if (updateForumArticleDTO.getCoverImg() != null) {
            // 这里打算用UUID生成文件名
            String uuid = MyStringUtil.getUUID();
            // 文件名为UUID+文件名后缀
            String suffix = updateForumArticleDTO.getCoverImg().getOriginalFilename()
                    .substring(updateForumArticleDTO.getCoverImg().getOriginalFilename().lastIndexOf(".")); //.png
            String imgName = uuid + suffix;
            System.out.println("文件名为：" + imgName);

            String imagePath = null;
            try {
                imagePath = minioService.uploadFile(updateForumArticleDTO.getCoverImg(), Constants.MINIO_ARTICLE_COVER_IMAGE_BUCKET_NAME, imgName);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            // 更新文章的封面
            forumArticle.setCover(imagePath);
        }

        // 再将文章的附件上传到minio
        if(updateForumArticleDTO.getAttachmentFile() != null){

            // 这里打算用UUID生成文件名
            String uuid = MyStringUtil.getUUID();
            // 文件名为UUID+文件名后缀
            String suffix = updateForumArticleDTO.getAttachmentFile().getOriginalFilename()
                    .substring(updateForumArticleDTO.getAttachmentFile().getOriginalFilename().lastIndexOf(".")); //.png
            String imgName = uuid + suffix;
            System.out.println("文件名为：" + imgName);

            String attachmentPath = null;
            try {
                attachmentPath = minioService.uploadFile(
                        updateForumArticleDTO.getAttachmentFile(), Constants.MINIO_ARTICLE_ATTACHMENT_BUCKET_NAME, imgName);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            // 更新文章附件记录
            ForumArticleAttachment forumArticleAttachment = new ForumArticleAttachment();
            forumArticleAttachment.setArticleId(forumArticle.getArticleId());
            forumArticleAttachment.setUserId(ThreadLocalUtil.getCurrentUserId());
            forumArticleAttachment.setFileSize(updateForumArticleDTO.getAttachmentFile().getSize());
            forumArticleAttachment.setFileName(updateForumArticleDTO.getAttachmentFile().getOriginalFilename());
            forumArticleAttachment.setBucketName(Constants.MINIO_ARTICLE_ATTACHMENT_BUCKET_NAME);
            forumArticleAttachment.setObjectName(imgName);
            forumArticleAttachment.setDownloadCount(0);
            forumArticleAttachment.setFilePath(attachmentPath);
            forumArticleAttachment.setFileType(0);
            forumArticleAttachment.setIntegral(0);

            List<ForumArticleAttachment> attachmentListByArticleId = forumArticleAttachmentService.getForumArticleAttachmentListByArticleId(updateForumArticleDTO.getArticleId());
            if (attachmentListByArticleId == null ){ // 说明之前该文章没有附件，直接插入即可
                forumArticleAttachmentService.insert(forumArticleAttachment);
            } else { // 说明之前文章有附件，更新文章附件即可
                forumArticleAttachment.setFileId(attachmentListByArticleId.get(0).getFileId());
                forumArticleAttachmentService.update(forumArticleAttachment);
            }

        }

        // 先更新数据库中文章内容
        forumArticle.setLastUpdateTime(LocalDateTime.now());
        forumArticleService.update(forumArticle);

        // 再更新数据库中的文章相关配置
        ForumArticleSetting forumArticleSetting = forumArticleSettingService.selectByArticleId(updateForumArticleDTO.getArticleId());
        if (forumArticleSetting == null) { // 说明之前没有设置
            ForumArticleSetting f = new ForumArticleSetting();
            f.setArticleId(updateForumArticleDTO.getArticleId());
            f.setPreviewTheme(updateForumArticleDTO.getPreviewTheme());
            f.setCodeTheme(updateForumArticleDTO.getCodeTheme());
            forumArticleSettingService.insert(f);
        } else { // 说明之前配置过了，我们想修改文章配置
            forumArticleSetting.setPreviewTheme(updateForumArticleDTO.getPreviewTheme());
            forumArticleSetting.setCodeTheme(updateForumArticleDTO.getCodeTheme());
            forumArticleSettingService.update(forumArticleSetting);
        }

        // 然后更新es中的文章内容
        ForumArticle forumArticle1 = forumArticleService.getForumArticleByArticleId(forumArticle.getArticleId());
        elasticSearchService.addForumArticleDocument(forumArticle1);

        // 最后将更新后的文章返回
        return Result.success(forumArticle1);
    }

    // 根据用户ID获取发表的文章
    @GetMapping("/getForumArticleListByUserId")
    public Result getForumArticleListByUserId(ForumArticleDTO forumArticleDTO){
        System.out.println("接收到参数："+forumArticleDTO);
        // 获取文章列表
        List<ForumArticle> articleList = forumArticleService.getForumArticleListByUserId(forumArticleDTO.getUserId(),
                forumArticleDTO.getOrderType(), forumArticleDTO.getPageNum(), forumArticleDTO.getPageSize());
        // 获取文章总数
        Integer articleArticleListTotal = forumArticleService.getPostCountByUserId(forumArticleDTO.getUserId());

        ForumArticleVO forumArticleVO = new ForumArticleVO();
        forumArticleVO.setForumArticleList(articleList);
        forumArticleVO.setTotal(articleArticleListTotal);
        return Result.success(forumArticleVO);
    }

    // 根据文章ID删除文章（只有是当前用户是文章作者的时候才能删除文章）
    @DeleteMapping("/deleteForumArticleByArticleId")
    public Result deleteForumArticleByArticleId(ForumArticleDTO forumArticleDTO){
        System.out.println("接收到参数："+forumArticleDTO);
        ForumArticle forumArticle = forumArticleService.getForumArticleByArticleId(forumArticleDTO.getArticleId());
        if (!forumArticle.getUserId().equals(ThreadLocalUtil.getCurrentUserId())) {
            throw new RuntimeException("只有文章作者才可以删除文章");
        }
        forumArticleService.deleteForumArticleByArticleId(forumArticleDTO.getArticleId());
        return Result.success();
    }

    // 文章搜索
    @GetMapping("/searchForumArticle")
    public Result searchForumArticle(ForumArticleDTO forumArticleDTO, HttpServletResponse response) throws IOException {
        System.out.println("文章搜索参数:"+forumArticleDTO.toString());
        List<ESForumArticleDTO> forumArticleList = elasticSearchService.getForumArticleListByTitle(
                                                                                        forumArticleDTO.getTitle(),
                                                                                        forumArticleDTO.getOrderType(),
                                                                                        forumArticleDTO.getPageNum(),
                                                                                        forumArticleDTO.getPageSize());
        return Result.success(forumArticleList);
    }

    // 相关文章推荐
    @GetMapping("/getRelativeForumArticle")
    public Result getRelativeForumArticle(ForumArticleDTO forumArticleDTO, HttpServletResponse response) throws IOException {
        System.out.println("文章搜索参数:"+forumArticleDTO.toString());
        List<ESForumArticleDTO> forumArticleList = elasticSearchService.getForumArticleListByTitle(
                                                                                        forumArticleDTO.getTitle(),
                                                                                        forumArticleDTO.getOrderType(),
                                                                                        forumArticleDTO.getPageNum(),
                                                                                        forumArticleDTO.getPageSize());
        return Result.success(forumArticleList);
    }

    // 获取热点文章
    @GetMapping("/getHotArticle")
    public Result getHotArticle() {
        List<HotForumArticleVO> list = (List<HotForumArticleVO>) redisTemplate.opsForValue().get("hot_article_list");
        return Result.success(list);
    }

    // 下载附件
    @GetMapping("/downloadAttachment")
    public void downloadAttachment(ForumArticleAttachmentDTO forumArticleAttachmentDTO, HttpServletResponse response) throws Exception{
        System.out.println("前端传递过来的文件名为" + forumArticleAttachmentDTO);

        String fileName = forumArticleAttachmentDTO.getFileName();
        String bucketName = forumArticleAttachmentDTO.getBucketName();
        String objectName = forumArticleAttachmentDTO.getObjectName();

        // 如果想让浏览器弹出下载框，后端需要设置响应头信息
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));

        // 从minio获取文件下载流
        GetObjectResponse getObjectResponse = minioService.downloadFile(bucketName, objectName);

        // 写回Response
        getObjectResponse.transferTo(response.getOutputStream());
    }
}
